/*
 *  Copyright (c) 1998, Regents of the University of California
 *  All rights reserved.
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *      * Redistributions of source code must retain the above copyright
 *        notice, this list of conditions and the following disclaimer.
 *      * Redistributions in binary form must reproduce the above copyright
 *        notice, this list of conditions and the following disclaimer in the
 *        documentation and/or other materials provided with the distribution.
 *      * Neither the name of the University of California, Berkeley nor the
 *        names of its contributors may be used to endorse or promote products
 *        derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 *  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.demis.elf.ddlwriter;

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.sql.Types;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang.StringUtils;
import org.demis.elf.Column;
import org.demis.elf.ColumnReference;
import org.demis.elf.ColumnSortSequence;
import org.demis.elf.ForeignKey;
import org.demis.elf.Index;
import org.demis.elf.PrimaryKey;
import org.demis.elf.Schema;
import org.demis.elf.Table;
import org.demis.elf.UniqueConstraint;

/**
 *
 * @version 1.0
 * @author <a href="mailto:demis27@demis27.net">Stéphane kermabon</a>
 */
public class DDLWriter {

    public static void write(Schema schema, String outputFilename) {
        try {
            FileWriter writer = new FileWriter(outputFilename);

            write(schema, writer);
        } catch (IOException ex) {
            Logger.getLogger(DDLWriter.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public static void write(Schema schema, Writer writer) {
        try {
            writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
            writer.write("<schema name=\"" + schema.getName() + "\">\n");
            if (schema.getTables() != null) {
                for (Table table : schema.getTables()) {
                    writeTable(table, writer);
                }
            }
            writer.write("</schema>");
        } catch (IOException ex) {
            Logger.getLogger(DDLWriter.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private static void writeTable(Table table, Writer writer) throws IOException {
        writer.write("    <table name=\"" + table.getName() + "\">\n");
        if (!StringUtils.isEmpty(table.getRemarks())) {
            writer.write("        <remarks><![CDATA[" + table.getRemarks() + "]]></remarks>\n");
        }
        if (table.getColumns() != null) {
            for (Column column : table.getColumns()) {
                writeColumn(column, writer);
            }
        }
        if (table.getPrimaryKey() != null) {
            writePrimaryKey(table.getPrimaryKey(), writer);
        }
        if (table.getIndexes() != null) {
            for (Index index : table.getIndexes()) {
                writeIndex(index, writer);
            }
        }
        if (table.getImportedKeys() != null) {
            for (ForeignKey foreignKey : table.getImportedKeys()) {
                writeForeignKey(foreignKey, writer);
            }
        }
        if (table.getUniqueConstraints() != null) {
            for (UniqueConstraint uniqueConstraint : table.getUniqueConstraints()) {
                writeUniqueConstraint(uniqueConstraint, writer);
            }
        }
        writer.write("    </table>\n");
    }

    private static void writeColumn(Column column, Writer writer) throws IOException {
        writer.write("        <column name=\"" + column.getName() + "\" ");
        writer.write("isUnique=\"" + column.isUnique() + "\" ");
        writer.write("isPrimaryKey=\"" + column.isPrimaryKey() + "\" ");
        writer.write("isNotNull=\"" + column.isNotNull() + "\" ");
        if (column.getSize() > 0) {
            writer.write("size=\"" + column.getSize() + "\" ");
        }
        if (column.getScale() > 0) {
            writer.write("scale=\"" + column.getScale() + "\" ");
        }
        writer.write("isForeignKey=\"" + column.isForeignKey() + "\" ");
        writer.write("dataType=\"" + getDataType(column.getType()) + "\" ");
        writer.write("position=\"" + column.getPosition() + "\" ");

        writer.write(">\n");
        if (!StringUtils.isEmpty(column.getRemarks())) {
            writer.write("            <remarks><![CDATA[" + column.getRemarks() + "]]></remarks>\n");
        }
        if (!StringUtils.isEmpty(column.getDefaultValue())) {
            writer.write("            <defaultValue><![CDATA[" + column.getDefaultValue() + "]]></defaultValue>\n");
        }
        writer.write("        </column>\n");
    }

    private static void writePrimaryKey(PrimaryKey primaryKey, Writer writer) throws IOException {
        writer.write("        <primaryKey name=\"" + primaryKey.getName() + "\">\n");
        if (primaryKey.getColumns() != null) {
            int order = 0;
            for (Column column : primaryKey.getColumns()) {
                writer.write("            <columnRef name=\"" + column.getName() + "\" order=\"" + order++ + "\"/>\n");
            }
        }
        writer.write("        </primaryKey>\n");
    }

    private static void writeIndex(Index index, Writer writer) throws IOException {
        writer.write("        <index name=\"" + index.getName() + "\" unique=\"" + index.isUnique() + "\"");
        if (index.getColumnSortSequence() != null && index.getColumnSortSequence() != ColumnSortSequence.NO_SORT) {
            writer.write(" columnSortSequence=\"" + index.getColumnSortSequence().getLabel() + "\"");
        }
        writer.write(">\n");
        if (index.getColumns() != null) {
            int order = 0;
            for (Column column : index.getColumns()) {
                writer.write("            <columnRef name=\"" + column.getName() + "\" order=\"" + order++ + "\"/>\n");
            }
        }
        writer.write("        </index>\n");
    }

    private static void writeForeignKey(ForeignKey foreignKey, Writer writer) throws IOException {
        writer.write("        <foreignKey name=\"" + foreignKey.getName() + "\" referencedTable=\"" + foreignKey.getExportedTable().getName() + "\">\n");
        if (foreignKey.getImportedColumns() != null) {
            for (Column importedColumn : foreignKey.getImportedColumns()) {
                writer.write("            <referencedColumn from=\"" + importedColumn.getName() + "\" to=\"" + foreignKey.getExportedColumn(importedColumn).getName() + "\"/>\n");
            }
        }
        writer.write("        </foreignKey>\n");
    }

    private static void writeUniqueConstraint(UniqueConstraint uniqueConstraint, Writer writer) throws IOException {
        writer.write("        <uniqueConstraint name=\"" + uniqueConstraint.getName() + "\">\n");
        if (uniqueConstraint.getColumnReferences() != null) {
            for (ColumnReference columnReference : uniqueConstraint.getColumnReferences()) {
                writer.write("            <columnRef name=\"" + columnReference.getColumn().getName() + "\" order=\"" + columnReference.getOrder() + "\"/>\n");
            }
        }
        writer.write("        </uniqueConstraint>\n");
    }

    public static String getDataType(int dataType) {
        switch (dataType) {
            case Types.ARRAY        : return "array" ;

            case Types.BIGINT       : return "bigint" ;
            case Types.BINARY       : return "binary" ;
            case Types.BIT          : return "bit" ;
            case Types.BLOB         : return "blob" ;
            case Types.BOOLEAN      : return "boolean" ;

            case Types.CHAR         : return "char" ;
            case Types.CLOB         : return "clob" ;

            case Types.DATALINK     : return "datalink" ;
            case Types.DATE         : return "date" ;
            case Types.DECIMAL      : return "decimal" ;
            case Types.DISTINCT     : return "distinct" ;
            case Types.DOUBLE       : return "double" ;

            case Types.FLOAT        : return "float" ;

            case Types.INTEGER      : return "integer" ;

            case Types.JAVA_OBJECT  : return "java_object" ;

            case Types.LONGNVARCHAR : return "longnvarchar" ;
            case Types.LONGVARBINARY: return "longvarbinary" ;
            case Types.LONGVARCHAR  : return "longvarchar" ;

            case Types.NCHAR        : return "nchar" ;
            case Types.NCLOB        : return "nclob" ;
            case Types.NUMERIC      : return "numeric" ;
            case Types.NVARCHAR     : return "nvarchar" ;

            case Types.REAL         : return "real" ;
            case Types.REF          : return "ref" ;
            case Types.ROWID        : return "rowid" ;

            case Types.SMALLINT     : return "smallint" ;
            case Types.SQLXML       : return "sqlxml" ;
            case Types.STRUCT       : return "struct" ;

            case Types.TIME         : return "time" ;
            case Types.TIMESTAMP    : return "timestamp" ;
            case Types.TINYINT      : return "tinyint" ;

            case Types.VARBINARY    : return "varbinary" ;
            case Types.VARCHAR      : return "varchar" ;

            default: return "unknown";
        }
    }

}
